﻿/* HEADER
 * ------
 * © 2009 by Salomon Zwecker 
 * modified by:
 * - 
 */
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

namespace Shapes.Geometry
{
    /// <summary>
    /// a line drawing
    /// </summary>
    public sealed class Line : Drawing
    {
        internal Vector2 _StartPoint, _EndPoint;
        /// <summary>
        /// One end of the Line.
        /// </summary>
        public Vector2 StartPoint { 
            get { return _Transform.TransformLocalToGlobal(_StartPoint); }
            set { SetPoints(value, _Transform.TransformLocalToGlobal(_EndPoint), false); } 
        }
        /// <summary>
        /// One end of the Line.
        /// </summary>
        public Vector2 EndPoint {
            get { return _Transform.TransformLocalToGlobal(_EndPoint); }
            set { SetPoints(_Transform.TransformLocalToGlobal(_StartPoint), value, false); } 
        }

        /// <summary>
        /// creates a line drawing
        /// </summary>
        /// <param name="startPoint">the start position</param>
        /// <param name="endPoint">the end position</param>
        public Line(Vector2 startPoint, Vector2 endPoint)
            : base()
        {
            _Transform = new Transformation2D();
            SetPoints(startPoint, endPoint);
        }

        /// <summary>
        /// a test to check for the distance of a point to the nearest edge
        /// </summary>
        /// <param name="point">the point to check</param>
        /// <returns>the distance to the nearest edge</returns>
        internal override float GetDistanceToEdge(ref Vector2 point)
        {
            Vector2 dir = Vector2.Normalize(_EndPoint - _StartPoint);
            float perp = Vector2.Dot(dir, (point - _StartPoint) / Vector2.Dot(dir, dir));

            if (perp < 0)
                return Vector2.Distance(point, _StartPoint);

            if (perp > Vector2.Distance(_StartPoint, _EndPoint))
                return Vector2.Distance(point, _EndPoint);

            Vector2 perpPoint = _StartPoint + perp * dir;
            return Vector2.Distance(point, perpPoint);;
        }

        internal override Vector2 GetNearestPointOnEdge(ref Vector2 point)
        {
            return Line.GetClosestPoint(ref point, ref _StartPoint, ref _EndPoint);
        }
        internal static Vector2 GetClosestPoint(ref Vector2 point, ref Vector2 start, ref Vector2 end)
        {
            Vector2 dir = Vector2.Normalize(end - start);
            float perp = Vector2.Dot(dir, (point - start) / Vector2.Dot(dir, dir));
            perp = Math.Max(0, Math.Min(perp, Vector2.Distance(start, end)));

            return start + perp * dir;
        }

        internal override Vector2 GetPositionFromT(float t)
        {
            return Vector2.Lerp(_StartPoint, _EndPoint, t);
        }

        internal override float GetEdgePathValueFromPoint(ref Vector2 point)
        {
            return (point - _StartPoint).LengthSquared() / (_EndPoint - _StartPoint).LengthSquared();
        }
        /// <summary>
        /// Resets the start and end point of the line in global coordinates
        /// </summary>
        /// <param name="start">the new start point</param>
        /// <param name="end">the new end point</param>
        public void SetPoints(Vector2 start, Vector2 end)
        {
            SetPoints(start, end, false);
        }
        /// <summary>
        /// Resets the start and end point of the line
        /// </summary>
        /// <param name="start">the new start point</param>
        /// <param name="end">the new end point</param>
        /// <param name="isLocalCoordinate">true, if the passed coordinates are in local space</param>
        public void SetPoints(Vector2 start, Vector2 end, bool isLocalCoordinate)
        {
            if (!isLocalCoordinate)
            {
                _Transform.TransformGlobalToLocal(ref start);
                _Transform.TransformGlobalToLocal(ref end);
            }

            Vector2 min = new Vector2(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));

            _StartPoint = start - min;
            _EndPoint = end - min;

            _Transform._Position += min;
            _Transform._Width = Math.Max(_StartPoint.X, _EndPoint.X);
            _Transform._Height = Math.Max(_StartPoint.Y, _EndPoint.Y);

            _BorderLength = Vector2.Distance(_StartPoint, _EndPoint);

            CallOnChange();
        }

        internal override Vector2 GetTangent(ref Vector2 position)
        {
            return Vector2.Normalize(_EndPoint - _StartPoint);
        }

        internal override Vector2 GetNormal(ref Vector2 position)
        {
            Vector2 tangent = GetTangent(ref position);
            return new Vector2(-tangent.Y, tangent.X);
        }

        /// <summary>
        /// Clones the Line.
        /// </summary>
        /// <returns>an object with the type Line</returns>
        public override object Clone()
        {
            Line l = new Line(_StartPoint + _Transform._Position, _EndPoint + _Transform._Position);
            l._Transform = (Transformation2D)_Transform.Clone();
            return l;
        }

        /// <summary>
        /// returns a string identifying the line
        /// </summary>
        /// <returns>a string identifying the line</returns>
        public override string ToString()
        {
            return "Drawing.Line start:" + StartPoint.ToString() + " end:" + EndPoint.ToString();
        }

        /// <summary>
        /// Gets the enumeration type which is mapped to this kind of object
        /// </summary>
        /// <returns>the geometry typee of this object</returns>
        public override GeometryType GetGeometryType()
        {
            return GeometryType.Line;
        }

        /// <summary>
        /// converts the geometry to a LineStrip.
        /// </summary>
        /// <returns>a line strip object</returns>
        public override LineStrip ToLineStrip()
        {
            return new LineStrip(StartPoint, Vector2.Lerp(StartPoint, EndPoint, 0.5f), EndPoint);
        }
    }
}
